package org.naxx.security.parser;

import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;

/**
 * All XML parsers should implement this class. Useful methods
 * in XML parsing are defined here.
 */
public abstract class AbstractXmlParser<T> {

	public abstract T parse(XMLEventReader reader) throws Exception;
	
	/**
	 * Read characters by skipping comments and trimming leading and
	 * trailing spaces. <p>
	 * 
	 * Precondition: cursor points to opening element.<br>
	 * Postcondition: cursor points to closing element.
	 */
	protected final XMLEvent getCharacters(XMLEventReader reader, StringBuilder buffer) throws XMLStreamException {
		XMLEvent event = reader.nextEvent();
		while(!(event.getEventType() == XMLStreamConstants.END_ELEMENT)) {
			while(event.getEventType() == XMLStreamConstants.COMMENT) {
				event = reader.nextEvent();
			}
			if(!(event.getEventType() == XMLStreamConstants.END_ELEMENT)) {
				buffer.append(event.asCharacters().getData());
			}
			event = reader.nextEvent();
		}
		return event;
	}
	
	/**
	 * Move cursor to the last element before either start
	 * or end event.
	 */
	protected final void skipCharactersComments(XMLEventReader reader) throws Exception {
		XMLEvent event = reader.peek();
		while(event.getEventType() == XMLStreamConstants.CHARACTERS ||
				event.getEventType() == XMLStreamConstants.COMMENT) {
			reader.nextEvent();
			event = reader.peek();
		}
	}
	
	/**
	 * Call to <i>reader.next()</i> after this method finishes will return first
	 * start element of the XML fragment.
	 */
	public void skipDocumentStart(XMLEventReader reader) throws XMLStreamException {
		while(reader.peek().isStartDocument()) {
			reader.nextEvent();
		}
	}
	
	/**
	 * Get next end or start tag and skip all characters, processing 
	 * instructions, and entity references.
	 */
	protected XMLEvent nextTag(XMLEventReader reader) throws XMLStreamException {
		XMLEvent event = reader.peek();
		while(event.isCharacters() || event.isProcessingInstruction() ||
				event.isEntityReference()) {
			event = reader.nextEvent();
			event = reader.peek();
		}
		return reader.nextTag();
	}
}
